/* -*- c++ -*-
   llvmir.h - LLVMIR main header 
   Copyright (C) 2011 Matthew Wahab <mwb.cde@gmail.com>

   This file is part of GCC-LLVMIR.

   GCC-LLVMIR is free software; you can redistribute it and/or modify it under
   the terms of the GNU General Public License as published by the Free
   Software Foundation; either version 3, or (at your option) any later
   version.

   GCC-LLVMIR is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   for more details.

   You should have received a copy of the GNU General Public License
   along with GCC-LLVMIR; see the file COPYING3.  If not see
   <http://www.gnu.org/licenses/>.  */

/* This is a C++ file. */

#ifndef LLVMIR_H
#define LLVMIR_H

#include <cstdlib> 
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include <list>

#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Type.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Function.h"
#include "llvm/Pass.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/raw_os_ostream.h"
#include "llvm/Assembly/Parser.h"
#include "llvm/Assembly/PrintModulePass.h"

#include "llvmir-system.h"

#include "SimpleList.h"
#include "repr.h"

/*
 * LLVMIR definitions
 */

namespace llvmir
{
  /*
   * Common types and structures
   */

  /* Mapping for value aliases. */
  typedef std::map<const llvm::Value*, const llvm::Value*> AliasMap;

  /* Representation to use for aliased values. */
  typedef std::map<const llvm::Value*, ValueRepr*> AliasReprMap;

  /* String vector */
  typedef std::vector<std::string> StringSeq;

  /*
   * Utility functions.
   */

  /* Allocate an array of strings, with the contents of the
     vector. The number of elements in the array is the same as the
     number of strings in the vector.
   */
  const char** 
  array_of_strseq(const StringSeq& string_seq);

  /*
   * Front-end translation context
   */

  /* Known input languages */
  enum LangKind
    {
      LANG_ASM = 0,
      LANG_CLANG = 1,
      LANG_OTHER = 2
    };

  /* Handle objects in some scope. Store and look-up objects, delete
     objects when the scope ends. */
  class Scope
  {
  protected:
    /* Fields */

    // Symbol table mapping LLVM types to trees. 
    TypeReprMap type_repr_map;

    // Symbol table mapping LLVM values to their representation.
    ValueReprMap value_repr_map;

    // List of TypeRepr objects created.
    TypeReprTrackerList type_repr_objects;
    
    // List of ValueRepr objects created.
    ValueReprTrackerList value_repr_objects;

    // LLVM value alias definitions.
    AliasMap llvm_alias_map;

    // LLVM value alias representations.
    AliasReprMap alias_repr_map;

  public:
    /* Constructor */
    explicit Scope()
    { /* skip */ }

    /* Destructor */
    ~Scope();

    /* Methods */

    // Clear a scope, deleting all tracked objects. */
    void clear();

    // Look-up a value.
    ValueRepr* find_value(const llvm::Value* llvm_value);

    // Store a value.
    ValueRepr* store_value(const llvm::Value* llvm_value,
                           ValueRepr* value_repr);

    // Remove a value.
    void remove_value(const llvm::Value* llvm_value);

    // Track a value.
    ValueRepr* track_value_repr(ValueRepr* value_repr);

    // Stop tracking a value.
    ValueRepr* ignore_value_repr(ValueRepr* value_repr);

    // Register value (store and track a value).
    ValueRepr* register_value_repr(ValueRepr* value_repr);

    // Drop value representation (don't store or track a value).
    void drop_value_repr(ValueRepr* value_repr);

    // Look-up a type.
    TypeRepr* find_type(const llvm::Type* llvm_type);

    // Store a type.
    TypeRepr* store_type(const llvm::Type* llvm_type,
                         TypeRepr* type_repr);

    // Remove a type.
    void remove_type(const llvm::Type* llvm_type);

    // Track a type.
    TypeRepr* track_type_repr(TypeRepr* type_repr);

    // Stop tracking a type.
    TypeRepr* ignore_type_repr(TypeRepr* type_repr);

    // Register type (store and track a type).
    TypeRepr* register_type_repr(TypeRepr* type_repr);

    // Drop type representation (don't store or track a type).
    void drop_type_repr(TypeRepr* type_repr);

    // Look-up an alias definition, return 0 if not found.
    const llvm::Value* find_alias(const llvm::Value* llvm_value);

    // Store an alias definition.
    const llvm::Value* store_alias(const llvm::Value* llvm_value, 
                                   const llvm::Value* llvm_def);

    // Remove an alis definition.
    void remove_alias(const llvm::Value* llvm_value);

    // Look-up an alias representation, return 0 if not found.
    ValueRepr* find_alias_repr(const llvm::Value* llvm_value);

    // Store an alias representation.
    ValueRepr* store_alias_repr(const llvm::Value* llvm_value, 
                                ValueRepr* value_repr);

    // Remove an alias represenation.
    void remove_alias_repr(const llvm::Value* llvm_value);
  };

  /* A translation context holds information used when converting a
     specific type of object. */
  class ObjContext
  {
    /* Types */
  public:
    enum Kind
      {
        GENERIC,    // Generic context.
        FUNCTION,   // Function context.
        UNIT        // Translation unit context.
      };

    /* Fields */
  protected:
    // The context type.
    Kind kind_;

  public:
    // Current location in the source file, used for information
    // messages only.
    location_t src_loc;

    // The scope for this context.
    Scope scope;

    /* Methods */
  protected:
    // Private constructor
    explicit 
    ObjContext(Kind k, location_t l):
      kind_(k), src_loc(l)
    { /* skip */ }

  public:
    // Constructor
    explicit ObjContext():
      kind_(GENERIC), src_loc(UNKNOWN_LOCATION)
    { /* skip */ }

    // Destructor (in llvmir.cc)
    virtual ~ObjContext();

    // Get the type of context.
    Kind kind()
    {
      return kind_;
    }

    // Look-up a stored conversion for a value, return 0 if not found.
    ValueRepr*
    lookup_value(const llvm::Value* llvm_value);

    // Register (store and track) a value representation.
    ValueRepr*
    register_value_repr(ValueRepr* value_repr);

    // Drop a stored conversion for a value.
    void
    drop_value_repr(ValueRepr* value_repr);
    
    // Look-up a stored conversion for a type, return 0 if not found.
    TypeRepr*
    lookup_type(const llvm::Type* llvm_type);

    // Register (store and track) a type representation.
    TypeRepr*
    register_type_repr(TypeRepr* type_repr);

    // Drop a stored conversion for a type.
    void
    drop_type_repr(TypeRepr* type_repr);

    /* Reset the context */
    virtual void reset();
  };

  /* FnContext holds information used when building up a function
     definition. */
  class FnContext: public ObjContext
  {
  public:
    /* Fields */

    // The function being defined.
    FunctionRepr* fn_repr;

    /* Constructor */
    explicit FnContext()
      : ObjContext(FUNCTION, UNKNOWN_LOCATION), fn_repr(0)
    { /* skip */ }

    /* Destructor */
    ~FnContext()
    { /* skip */ }

    /* Methods */

    // Cast from a generic context.
    static FnContext* from(ObjContext* obj_context);

    // Set-up for a new function.
    void set_function(FunctionRepr* fr);

    // Reset the function context.
    void reset();
  };

  /* UnitContext holds information used when converting a top-level
     translation unit.*/
  class UnitContext: public ObjContext
  {
  public:
    /* Fields */

    // The toplevel declaration, in which module variables and
    // functions are declared.
    tree unit_decln_tree;

    // The toplevel scope, for module variables.
    tree unit_scope_tree;

    /* Constructor */
    explicit UnitContext()
      : ObjContext(UNIT, UNKNOWN_LOCATION),
        unit_decln_tree(NULL_TREE),
        unit_scope_tree(NULL_TREE)
    { /* skip */ }

    /* Destructor */
    ~UnitContext()
    { /* skip */ }

    /* Methods */

    // Cast from a generic context
    static UnitContext* from(ObjContext* obj_context);

    // Reset the translation unit context.
    void reset();
  };

  /*
   * Top-level structures.
   */

  /* Forward declaration of parsers */
  class Parser;

  /* Global options */
  struct Options
  {
  public:
    /* Fields */

    // The input language.
    LangKind language;

    // Parser options, passed in llvmir-opt.
    StringSeq parser_opts;

    // Include directories.
    StringSeq include_dirs;

    // Library directories.
    StringSeq library_dirs;

    // Only check the syntax.
    bool syntax_only;

    /* Debug options */
    
    // How much output to generate.
    int debug_output;

    // Generic debug option.
    int debug_value;

    /* Methods */

    // Reset the options to the default values.
    void clear();

    /* Constructor */
    explicit Options()
    { 
      clear();
    }

    /* Destructor */
    ~Options()
    { /* skip */ }
  };

  /* Main translation context. */
  class Context
  {
  public:
    /* Fields */

    // Global options.
    Options* options;

    // The parser to use.
    Parser* parser;

    // Pointer to the input file name or null.
    const char* input_file_name;

    // The LLVM context.
    llvm::LLVMContext llvm_context;

    // The context for the top-level translation unit currently being
    // converted.
    UnitContext unit_context;

    // The context for the object currently being converted or null.
    ObjContext* curr_context;

    /* Constructor */
    Context():
      options(0), 
      input_file_name(0),
      curr_context(0)
    { /* skip */ }

    /* Destructor */
    ~Context();

    /* Methods */

    // Get the parser to use.
    Parser* 
    get_parser();

    // Look-up a stored conversion for a value, return 0 if not found.
    ValueRepr*
    lookup_value(const llvm::Value* llvm_value);

    // Register (store and track) a value representation.
    ValueRepr*
    register_value_repr(ValueRepr* value_repr);

    // Drop a stored conversion for a value, returning NULL if not
    // available.
    void
    drop_value_repr(ValueRepr* value_repr);
    
    // Look-up a stored conversion for a type, returning 0 if not
    // found.
    TypeRepr*
    lookup_type(const llvm::Type* llvm_type);

    // Register (store and track) a type representation.
    TypeRepr*
    register_type_repr(TypeRepr* type_repr);

    // Drop a stored conversion for a type, returning NULL if not
    // available.
    void
    drop_type_repr(TypeRepr* type_repr);

    // Store a value alias.
    const llvm::Value* 
    store_alias(const llvm::Value* llvm_value,
                const llvm::Value* llvm_def);

    // Look-up a value alias, return 0 if not found.
    const llvm::Value* 
    lookup_alias(const llvm::Value* llvm_value);

    // Look-up a value alias, return 0 if not found.
    ValueRepr*
    lookup_alias_repr(const llvm::Value* llvm_value);

    // Store a value alias.
    ValueRepr*
    store_alias_repr(const llvm::Value* llvm_value,
                     ValueRepr* value_repr);
  };

  /*
   * Tree utility functions (convert.cc).
   */
  namespace Tree
  {
    /* Generate an identifier tree. If the name is artificial, its
       name will be prefixed with llvmir_name_prefix.*/
    tree 
    make_name_tree(const std::string& name, bool artificial);

    /* Build and declare a label. */
    tree 
    declare_label_tree(location_t loc, tree label_name,
                       tree context, bool artificial);


    /* Build a variable declaration. If the variable is artificial,
       its name will be prefixed with llvmir_name_prefix. */
    tree
    build_var_decl(location_t loc,
                   const std::string& name,
                   tree var_type,
                   bool artificial);

    /* Add a variable declaration to a block scope. */
    void 
    declare_var(tree new_var, tree context, tree block);

    /* Make a boolean value. */
    tree
    make_bool_tree(bool val);

    /* Make a string, with a given type.  */
    tree 
    build_string(tree str_type, int length, const char* str);

    /* Make an array type with an arbitrary range. */
    tree 
    make_array_type(tree elem_type, tree range);

    /* Make an array type with an integer range */
    tree 
    make_array_type_cst(tree elem_type, size_t num_elems);

    /* Make an array reference (array[idx]). */
    tree
    make_array_ref(location_t loc, tree array, tree index);

    /* Make an array reference (array[idx]) with a constant index. */
    tree
    make_array_ref_cst(location_t loc, tree array, unsigned elt);

    /* 
       Vector operations. Vectors are represented as single
       dimensioned arrays.
    */

    /* Test whether a type is LLVMIR's idea of a vector type. */
    bool
    is_vector_type(tree type);

    /* Test whether a value has LLVMIR's idea of a vector type. */
    bool 
    has_vector_type(tree expr);

    /* Get the element type from LLVMIR's idea of a vector type. */
    bool
    vector_base_type(tree vec_type);

    /* Make an vector type with an arbitrary range. */
    tree 
    make_vector_type(tree elem_type, tree range);

    /* Make an vector type with an integer range */
    tree 
    make_vector_type_cst(tree elem_type, size_t num_elems);

    /* Make an vector reference (vector[index]). */
    tree
    make_vector_ref(location_t loc, tree vector, tree index);

    /* Make an vector reference (vector[index]) with a constant
       index. */
    tree
    make_vector_ref_cst(location_t loc, tree vector, unsigned elt);

    /* Map an operation to the elements of a vector. The operation is
       passed in as a VecMapper object and used to construct a
       vector of element type base_type and size n with the form:

       CONSTRUCTOR < n x base_type >
       {
       VecMapper.build(0)
       ...
       VecMapper.build(i)         
       ...
       VecMapper.build(n - 1)
       }
    */
    class VecMapper
    {
    public:
      // Destructor
      virtual ~VecMapper();
      // build(idx): Form the element at index idx
      virtual tree build(unsigned idx) = 0;
    };

    /* Construct a vector of size sz, by applying a mapper. */
    tree make_mapped_vector(tree base_type, const unsigned sz,
                            VecMapper* mapper);


    /* Vector operation constants mapper. */
    class VecMapConst : public Tree::VecMapper
    {
    public:
      // Fields
      location_t loc;
      tree value;

      // Constructor
      explicit VecMapConst(location_t l, tree v)
        : loc(l), value(v)
      { /* skip */}

      tree build(const unsigned idx ATTRIBUTE_UNUSED);
    };

    /* Vector operation mapper, for unary operations. */
    class VecMapUnOp : public Tree::VecMapper
    {
    public:
      // Fields
      location_t loc;
      tree_code opcode;
      tree type;
      tree expr1;

      // Constructor
      explicit 
      VecMapUnOp(location_t l, tree_code op, tree ty, tree e1)
        : loc(l), opcode(op), type(ty), expr1(e1)
      { /* skip */ }

      tree build(const unsigned idx);
    };

    /* Vector operation mapper, for simple binary operations. */
    class VecMapBinOp : public Tree::VecMapper
    {
    public:
      // Fields
      location_t loc;
      tree_code opcode;
      tree type;
      tree expr1;
      tree expr2;

      // Constructor
      explicit 
      VecMapBinOp(location_t l, tree_code op, tree ty, tree e1, tree e2)
        : loc(l), opcode(op), type(ty), expr1(e1), expr2(e2)
      { /* skip */ }

      // Construct the true result
      virtual tree true_result(const unsigned idx);
      // Construct the false result
      virtual tree false_result(const unsigned idx);
      tree build(const unsigned idx);
    };

    /* Mapper to apply a predicated update to a vector. 
       Applies the operation (sel[idx] ? expr1[idx]: expr2[idx]).
    */
    class VecMapPred: public Tree::VecMapper
    {
    public:
      location_t loc;
      tree type;
      tree expr1;
      tree expr2;

      explicit 
      VecMapPred(location_t l, tree ty, tree e1, tree e2)
        : loc(l), type(ty),  expr1(e1), expr2(e2)
      { /* skip */ }

      // Construct the predicate.
      virtual tree pred(const unsigned idx ATTRIBUTE_UNUSED);
      // Construct the true result
      virtual tree true_result(const unsigned idx);
      // Construct the false result
      virtual tree false_result(const unsigned idx);
      // Construct the result.
      tree build(unsigned idx);
    };

    /* 
     * Functions that appear in gcc.git but not gcc-4.6 
     */
    tree build_case_label(tree low, tree high, tree decl);

    /* Get declaration of the specified built-in or NULL if not known. */
    tree builtin_decl(enum built_in_function fncode);

  } // namespace Tree

  /* 
   * For debugging.
   */
  
  /* Print an LLVM module. */
  void print_module(llvm::Module* llvm_module);

  /* Print a generic tree, no pretty printing. */
  void dump_tree(tree decl);

  /* Print a function declaration */
  void debug_print_function(tree fndecl);

} // End of namespace llvmir

/* End of file. */
#endif /* LLVMIR_H */
